/**
 * @class FormField
 * @extends Widget
 * @param config
 *            {Object} Configuration object
 * @constructor
 * @description A representation of an individual form field.
 */
function FormField() {
	FormField.superclass.constructor.apply(this, arguments);
}

Y.mix(FormField, {

	/**
	 * @property FormField.NAME
	 * @type String
	 * @static
	 */
	NAME : 'form-field',

	/**
	 * @property FormField.ATTRS
	 * @type Object
	 * @protected
	 * @static
	 */
	ATTRS : {
		/**
		 * @attribute id
		 * @type String
		 * @default Either a user defined ID or a randomly generated by Y.guid()
		 * @description A randomly generated ID that will be assigned to the
		 *              field and used in the label's for attribute
		 */
		id : {
			value : Y.guid(),
			validator : Y.Lang.isString,
			writeOnce : true
		},

		/**
		 * @attribute name
		 * @type String
		 * @default ""
		 * @writeOnce
		 * @description The name attribute to use on the field
		 */
		name : {
			validator : Y.Lang.isString,
			writeOnce : true
		},

		/**
		 * @attribute value
		 * @type String
		 * @default ""
		 * @description The current value of the form field
		 */
		value : {
			value : '',
			validator : Y.Lang.isString
		},

		/**
		 * @attribute label
		 * @type String
		 * @default ""
		 * @description Label of the form field
		 */
		label : {
			value : '',
			validator : Y.Lang.isString
		},

		/**
		 * @attribute validator
		 * @type Function
		 * @default function () { return true; }
		 * @description Used to validate this field by the Form class
		 */
		validator : {
			value : function(val) {
				return true;
			},
			validator : Y.Lang.isFunction
		},

		/**
		 * @attribute required
		 * @type Boolean
		 * @default false
		 * @description Set true if this field must be filled out when submitted
		 */
		required : {
			value : false,
			validator : Y.Lang.isBoolean
		}
	},

	/**
	 * @property FormField.tabIndex
	 * @type Number
	 * @description The current tab index of all FormField instances
	 */
	tabIndex : 0,

	/**
	 * @property FormField.INPUT_TEMPLATE
	 * @type String
	 * @description Template used to draw an input node
	 */
	INPUT_TEMPLATE : '<input>',

	/**
	 * @property FormField.TEXTAREA_TEMPLATE
	 * @type String
	 * @description Template used to draw a textarea node
	 */
	TEXTAREA_TEMPLATE : '<textarea></textarea>',

	/**
	 * @property FormField.LABEL_TEMPLATE
	 * @type String
	 * @description Template used to draw a label node
	 */
	LABEL_TEMPLATE : '<label></label>',

	/**
	 * @property FormField.SELECT_TEMPLATE
	 * @type String
	 * @description Template used to draw a select node
	 */
SELECT_TEMPLATE : '<select></select>'
});
 
Y.extend(FormField, Y.Widget, {
	/**
	 * @property _labelNode
	 * @protected
	 * @type Object
	 * @description The label node for this form field
	 */
	_labelNode : null,

	/**
	 * @property _fieldNode
	 * @protected
	 * @type Object
	 * @description The form field itself
	 */
	_fieldNode : null,

	/**
	 * @property _errorNode
	 * @protected
	 * @type Object
	 * @description If a validation error occurs, it will be displayed in this
	 *              node
	 */
	_errorNode : null,

	/**
	 * @property _nodeType
	 * @protected
	 * @type String
	 * @description The type of form field to draw
	 */
	_nodeType : 'text',

	/**
	 * @method _renderLabelNode
	 * @protected
	 * @description Draws the form field's label node into the contentBox
	 */
	_renderLabelNode : function() {
		var contentBox = this.get('contentBox'), labelNode = contentBox
				.query('label');

		if (!labelNode || labelNode.get('for') != this.get('id')) {
			labelNode = Y.Node.create(FormField.LABEL_TEMPLATE);
			contentBox.appendChild(labelNode);
		}

		this._labelNode = labelNode;
	},

	/**
	 * @method _renderFieldNode
	 * @protected
	 * @description Draws the field node into the contentBox
	 */
	_renderFieldNode : function() {
		var contentBox = this.get('contentBox'), field = contentBox
				.query('#' + this.get('id'));

		if (!field) {
			field = Y.Node.create(FormField.INPUT_TEMPLATE);
			contentBox.appendChild(field);
		}

		this._fieldNode = field;
	},

	/**
	 * @method _syncLabelNode
	 * @protected
	 * @description Syncs the the label node and this instances attributes
	 */
	_syncLabelNode : function() {
		if (this._labelNode) {
			this._labelNode.setAttrs( {
				innerHTML : this.get('label')
			});
			this._labelNode.setAttribute('for', this.get('id'));
		}
	},

	/**
	 * @method _syncLabelNode
	 * @protected
	 * @description Syncs the fieldNode and this instances attributes
	 */
	_syncFieldNode : function() {
		this._fieldNode.setAttrs( {
			name : this.get('name'),
			type : this._nodeType,
			id : this.get('id'),
			value : this.get('value')
		});
		this._fieldNode.setAttribute('tabindex', FormField.tabIndex);
		FormField.tabIndex++;
	},

	/**
	 * @method _checkRequired
	 * @private
	 * @description if the required attribute is set to true, returns whether or
	 *              not a value has been set
	 * @return {Boolean}
	 */
	_checkRequired : function() {
		if (this.get('required') === true && this.get('value').length === 0) {
			return false;
		}
		return true;
	},

	/**
	 * @method showError
	 * @param {String}
	 *            errMsg
	 * @description Adds an error node with the supplied message
	 */
	showError : function(errMsg) {
		var contentBox = this.get('contentBox'), errorNode = Y.Node
				.create('<span>' + errMsg + '</span>');

		errorNode.addClass('error');
		contentBox.insertBefore(errorNode, this._labelNode);

		this._errorNode = errorNode;
	},

	/**
	 * @method clearError
	 * @description Removes the error node from this field
	 */
	clearError : function() {
		if (this._errorNode) {
			var contentBox = this.get('contentBox');
			contentBox.removeChild(this._errorNode);
			this._errorNode = null;
		}
	},

	/**
	 * @method validate
	 * @description Runs the validation functions of this form field
	 * @return {Boolean}
	 */
	validate : function() {
		var value = this.get('value'), validator = this.get('validator');

		this.clearError();

		if (!this._checkRequired()) {
			this.showError('This field is required');
			return false;
		}

		return validator.call(this, value);
	},

	/**
	 * @method clear
	 * @description Clears the value of this field
	 */
	clear : function() {
		this.set('value', '');
		this._fieldNode.set('value', '');
	},

	initializer : function() {
		this.publish('blur');
		this.publish('change');
		this.publish('focus');
	},

	destructor : function(config) {

	},

	renderUI : function() {
		this._renderLabelNode();
		this._renderFieldNode();
	},

	bindUI : function() {
		this._fieldNode.on('change', Y.bind(function(e) {
			this.set('value', this._fieldNode.get('value'));
		}, this));

		this.on('valueChange', Y.bind(function(e) {
			this._fieldNode.set('value', e.newVal);
		}, this));
	},

	syncUI : function() {
		this.get('boundingBox').removeAttribute('tabindex');
		this._syncLabelNode();
		this._syncFieldNode();
	}
});

Y.FormField = FormField;